#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#define MAXCAP 5


template<class T>
class BlockQueue
{
public:
    BlockQueue(size_t cap = MAXCAP) :_cap(cap)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_ccond, nullptr);
        pthread_cond_init(&_pcond, nullptr);
    }
    void push(const T& in)
    {
        pthread_mutex_lock(&_lock);
        // 1.判断  注意是用while，因为如何该线程又重新开始运行之前，可能别的生产者对阻塞队列进行了资源增加，导致阻塞队列又为满
        // 使用用while可以保持代码的健壮性
        while(isFull())
        {
            //因为生产条件不满住，无法生产，此时我们的生产者进行等待
            pthread_cond_wait(&_pcond, &_lock);
        }

        // 2.此时阻塞队列一定没有满，生产者开始生产
        _q.push(in);

        // 3.此时阻塞队列里一定有数据，发信号给消费者的条件变量
        pthread_cond_signal(&_ccond); //这里可以有一定的策略

        pthread_mutex_unlock(&_lock);

        //pthread_cond_signal(&_ccond);

        //sleep(1);
    }
    
    void pop(T* out)
    {
        pthread_mutex_lock(&_lock);

        // 1.判断  注意是用while，因为如何该线程又重新开始运行之前，可能别的消费者对阻塞队列资源进行使用，导致阻塞队列又为空
        // 使用用while可以保持代码的健壮性
        while(isEmpty())
        {
            pthread_cond_wait(&_ccond, &_lock); //该函数调用的时候，会以原子的方式，将锁释放，并将自己挂起
            //当调用该函数的线程被唤醒时候，会找到传入的锁，并加锁
        }

        // 2.此时该消费者线程可以进行消费资源了
        *out = _q.front();
        _q.pop();

        // 3.此时阻塞队列一定有空余位置，发信号给生产者条件变量
        pthread_cond_signal(&_pcond);

        pthread_mutex_unlock(&_lock);

    }
    //判断阻塞队列是否为空
    bool isEmpty()
    {
        return _q.empty();
    }

    //判断阻塞队列是否满了
    bool isFull()
    {
        return _q.size() == _cap;
    }


    ~BlockQueue()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_ccond);
        pthread_cond_destroy(&_pcond);
    }
private:
    std::queue<T> _q; //阻塞队列
    size_t _cap; //阻塞队列最大长度
    pthread_cond_t _ccond; //消费者条件变量
    pthread_cond_t _pcond; //生产者条件变量
    pthread_mutex_t _lock; //互斥锁
};


template<typename T1, typename T2>
class BlockQueues
{
public:
    BlockQueue<T1>* _cbq; //计算阻塞队列
    BlockQueue<T2>* _sbq; //存储阻塞队列
};